package com.eian.boot.common.aspect;

import com.eian.boot.common.annotation.OperationLog;
import com.eian.boot.common.core.model.model.LoginUser;
import com.eian.boot.common.core.utils.Ip2RegionUtil;
import com.eian.boot.common.core.utils.SpringContextUtil;
import com.eian.boot.common.enums.OperationStatusEnum;
import com.eian.boot.common.util.SensitiveDataMasker;
import com.eian.boot.common.util.ServletUtil;
import com.eian.boot.common.util.UserAgentParser;
import com.eian.boot.module.system.log.model.entity.SysOperationLogEntity;
import com.eian.boot.module.system.log.service.SysOperationLogService;
import com.eian.boot.security.utils.SessionHelper;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志AOP切面
 *
 * @author alex.meng
 */
@Slf4j
@Aspect
@Component
public class OperationLogAspect {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final int MAX_RESPONSE_LENGTH = 2000; // 响应结果最大长度

    @Around("@annotation(operationLog)")
    public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 构建操作日志实体
        SysOperationLogEntity logEntity = new SysOperationLogEntity();
        logEntity.setCreatedAt(LocalDateTime.now());

        try {
            // 设置基础信息
            buildBaseInfo(logEntity, joinPoint, operationLog);

            // 设置请求信息
            buildRequestInfo(logEntity, joinPoint, operationLog);

            // 设置操作人信息
            buildOperatorInfo(logEntity, operationLog);

            // 执行目标方法
            Object result = joinPoint.proceed();

            // 设置成功状态
            logEntity.setStatus(OperationStatusEnum.SUCCESS.getValue());

            // 设置响应结果
            if (operationLog.saveResponse() && result != null) {
                buildResponseInfo(logEntity, result);
            }

            return result;

        } catch (Throwable e) {
            // 设置失败状态
            logEntity.setStatus(OperationStatusEnum.FAIL.getValue());
            logEntity.setErrorMsg(e.getMessage() != null ? e.getMessage().substring(0, Math.min(e.getMessage().length(), 500)) : "");
            throw e;

        } finally {
            // 记录执行耗时
            logEntity.setCostTime(System.currentTimeMillis() - startTime);

            // 异步保存日志
            try {
                SysOperationLogService logService = SpringContextUtil.getBean(SysOperationLogService.class);
                logService.saveOperationLog(logEntity);
            } catch (Exception e) {
                log.error("保存操作日志失败", e);
            }
        }
    }

    /**
     * 构建基础信息
     */
    private void buildBaseInfo(SysOperationLogEntity logEntity, ProceedingJoinPoint joinPoint, OperationLog operationLog) {
        // 链路追踪ID
        String traceId = MDC.get("traceId");
        logEntity.setTraceId(traceId);

        // 操作模块
        logEntity.setModule(operationLog.module());

        // 业务类型
        logEntity.setBusinessType(operationLog.businessType().getValue());

        // 业务描述
        logEntity.setBusinessDesc(operationLog.businessDesc());

        // 方法信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        logEntity.setMethodName(method.getName());
        logEntity.setMethodClass(method.getDeclaringClass().getName());
    }

    /**
     * 构建请求信息
     */
    private void buildRequestInfo(SysOperationLogEntity logEntity, ProceedingJoinPoint joinPoint, OperationLog operationLog) {
        HttpServletRequest request = ServletUtil.getRequest();
        if (request != null) {
            // 请求方法和URL
            logEntity.setRequestMethod(request.getMethod());
            logEntity.setRequestUrl(request.getRequestURI());

            // IP和归属地
            String clientIp = ServletUtil.getClientIP();
            logEntity.setRequestIp(clientIp);
            try {
                logEntity.setRequestLocation(Ip2RegionUtil.getAddressByIp(clientIp));
            } catch (Exception e) {
                logEntity.setRequestLocation("未知");
            }

            // User-Agent解析
            String userAgent = ServletUtil.getUserAgent();
            logEntity.setUserAgent(userAgent);
            logEntity.setBrowser(UserAgentParser.parseBrowser(userAgent));
            logEntity.setOs(UserAgentParser.parseOS(userAgent));

            // 请求参数
            if (operationLog.saveRequest()) {
                try {
                    // Query参数
                    Map<String, String[]> parameterMap = request.getParameterMap();
                    if (parameterMap != null && !parameterMap.isEmpty()) {
                        String paramJson = OBJECT_MAPPER.writeValueAsString(parameterMap);
                        logEntity.setRequestParam(SensitiveDataMasker.maskJson(paramJson));
                    }

                    // Body参数
                    Object[] args = joinPoint.getArgs();
                    if (args != null && args.length > 0) {
                        Map<String, Object> bodyParams = new HashMap<>();
                        for (int i = 0; i < args.length; i++) {
                            Object arg = args[i];
                            // 跳过不需要序列化的类型
                            if (arg != null && !(arg instanceof HttpServletRequest)
                                    && !(arg instanceof MultipartFile)) {
                                bodyParams.put("arg" + i, arg);
                            }
                        }
                        if (!bodyParams.isEmpty()) {
                            String bodyJson = OBJECT_MAPPER.writeValueAsString(bodyParams);
                            logEntity.setRequestBody(SensitiveDataMasker.maskJson(bodyJson));
                        }
                    }
                } catch (Exception e) {
                    log.warn("解析请求参数失败", e);
                }
            }
        }
    }

    /**
     * 构建操作人信息
     */
    private void buildOperatorInfo(SysOperationLogEntity logEntity, OperationLog operationLog) {
        try {
            if (SessionHelper.isLogin()) {
                LoginUser loginUser = SessionHelper.getLoginUser();
                if (loginUser != null) {
                    logEntity.setOperatorId(loginUser.getId());
                    logEntity.setOperatorName(loginUser.getUsername());

                    // 填充部门信息
                    if (loginUser.getDeptId() != null) {
                        logEntity.setDeptId(loginUser.getDeptId());
                    }
                    if (loginUser.getDeptName() != null) {
                        logEntity.setDeptName(loginUser.getDeptName());
                    }
                }
            }
        } catch (Exception e) {
            log.warn("获取操作人信息失败", e);
        }

        // 操作类型
        logEntity.setOperatorType(operationLog.operatorType().getValue());
    }

    /**
     * 构建响应信息
     */
    private void buildResponseInfo(SysOperationLogEntity logEntity, Object result) {
        try {
            String resultJson = OBJECT_MAPPER.writeValueAsString(result);
            // 对响应结果进行脱敏
            resultJson = SensitiveDataMasker.maskJson(resultJson);

            // 限制长度
            if (resultJson.length() > MAX_RESPONSE_LENGTH) {
                resultJson = resultJson.substring(0, MAX_RESPONSE_LENGTH) + "...(truncated)";
            }

            logEntity.setResponseResult(resultJson);
        } catch (Exception e) {
            log.warn("解析响应结果失败", e);
            logEntity.setResponseResult("解析失败");
        }
    }
}

